#include "common.h"
#include "fundamental.h"
#include "object.h"

#include "asset.h"
#include "mesh.h"
#include "effect.h"
#include "body.h"

#include "actormanager.h"
#include "actor.h"

#include "scene.h"
#include "scenemanager.h"

#include "pawn.h"

IMPLEMENT_RTTI( Pawn, Actor )

Pawn::Pawn
(
	const std::string& type, 
	const std::string& name, 
	ActorTypeInfo &desc
)
:	Actor( type, name, desc )
,	_body( *(desc.body) )
{
}

Pawn::~Pawn()
{
}

Pawn* Pawn::Create( const std::string& type, const std::string& name )
{
	ActorManager::TypeList& types = ActorManager::_types;
	ActorManager::TypeListIter it = types.find( type );
	if( it == types.end() )
	{
		assert( false && "Register class first!" );
		return NULL;
	}
	if( it->second->body == NULL )
	{
		assert( false && "Pawn must have body!" );
		return NULL;
	}

	Pawn* pStat = new Pawn( type, name, *( it->second ) );
	pStat->_pParams = new lua_tinker::table( pStat->_desc.L );

	pStat->Call< void >( "Initialize" );

	return pStat;
}

void Pawn::Render( const std::string& techniqueName )
{
	LPDIRECT3DDEVICE9 device = Core::GetInstance().GetD3dDevice();

	LPD3DXEFFECT commonFx = NULL;
	D3DXHANDLE commonTech = NULL;
	if( _body.GetCommonFx() )
	{
		commonFx = _body.GetCommonFx()->GetEffect();
		commonTech = _body.GetCommonFx()->GetTechniqueInfos()[ techniqueName ];
	}

	std::vector< Body::Skin* >& skins = _body.GetSkins();

	static LPD3DXEFFECT prevFx = NULL;
	for( size_t i = 0; i < skins.size(); ++i )
	{
		LPDIRECT3DVERTEXDECLARATION9 decl = skins[i]->mesh->GetVertexDeclaration();
		
		if( skins[i]->effect )
		{
		}
		else
		{
			if( commonFx == NULL )
			{
				assert( false && "Must have common effect if skin doesn't have it's own effect!" );
			}
			if( prevFx != commonFx )
			{
				if( FAILED( commonFx->SetTechnique( commonTech ) ) )
				{
					assert( false );
				}
			}
			if( FAILED( commonFx->Begin( NULL, 0 ) ) ||
				FAILED( commonFx->BeginPass( 0 ) ) )
			{
				assert( false );
			}
			prevFx = commonFx;
		}

		SetEffectConsts( *commonFx );
		//this->Call< void >( "SetCubeColor", D3DXVECTOR3( 0, 1, 0 ) );

		LPDIRECT3DVERTEXBUFFER9 vb = skins[i]->mesh->GetVertexBuffer();
		D3DPRIMITIVETYPE pt = skins[i]->mesh->GetPrimitiveType();		

		device->SetVertexDeclaration( decl );
		device->SetStreamSource( 0, vb, 0, 24 );
		device->DrawPrimitive( pt, 0, 12 );

		if( skins[i]->effect )
		{
		}
		else
		{
			commonFx->EndPass();
			commonFx->End();
		}
	}
}

void Pawn::SetEffectConsts( ID3DXEffect& selectedFx )
{
	Effect::ConstantTable& consts = _body.GetCommonFx()->GetConstantInfos();
	Effect::ConstantTable::iterator it = consts.begin();

	for( ; it != consts.end(); ++it )
	{
		// common constants
		if( it->first == "matObj" )
		{
			D3DXHANDLE hMatObj = _body.GetCommonFx()->GetConstantHandle( "matObj" );
			if( hMatObj )
			{
				D3DXMATRIX matObj = this->GetMatObject();
				selectedFx.SetMatrix( hMatObj, &matObj );
			}
			continue;
		}
		if( it->first == "matView" )
		{
			Scene* pScene = Core::GetSceneManager().GetCurrentScene();
			Camera* pViewCam = pScene->GetViewCamera();
			D3DXHANDLE hMatView = _body.GetCommonFx()->GetConstantHandle( "matView" );
			if( hMatView )
			{
				D3DXMATRIX matView = pViewCam->GetViewMatrix();
				selectedFx.SetMatrix( hMatView, &matView );
			}
			continue;
		}
		if( it->first == "matProj" )
		{
			Scene* pScene = Core::GetSceneManager().GetCurrentScene();
			Camera* pViewCam = pScene->GetViewCamera();
			D3DXHANDLE hMatProj = _body.GetCommonFx()->GetConstantHandle( "matProj" );
			if( hMatProj )
			{
				D3DXMATRIX matProj = pViewCam->GetProjectionMatirx();
				selectedFx.SetMatrix( hMatProj, &matProj );
			}
			continue;
		}
		if( it->first == "matViewProj" )
		{
			Scene* pScene = Core::GetSceneManager().GetCurrentScene();
			Camera* pViewCam = pScene->GetViewCamera();
			D3DXHANDLE hMatViewProj = _body.GetCommonFx()->GetConstantHandle( "matViewProj" );
			if( hMatViewProj )
			{
				D3DXMATRIX matView = pViewCam->GetViewMatrix();
				D3DXMATRIX matProj = pViewCam->GetProjectionMatirx();
			}
			continue;
		}


		if( it->second.first == "float" )
		{ 
			float var = _pParams->get< float >( it->first.c_str() );
			D3DXHANDLE handle = _body.GetCommonFx()->GetConstantHandle( it->first.c_str() );
			if( handle )
			{
				selectedFx.SetFloat( handle, var );
				continue;
			}
			assert( false );
		}
		if( it->second.first == "float3" )
		{
			D3DXVECTOR3 var = _pParams->get< D3DXVECTOR3 >( it->first.c_str() );
			D3DXHANDLE handle = _body.GetCommonFx()->GetConstantHandle( it->first.c_str() );
			if( handle )
			{
				selectedFx.SetFloatArray( handle, &( *var ), 3 );
				continue;
			}
			assert( false );
		}
	}
}